package boboUtil;

public abstract class BoboAbstractMap<K,V> implements BoboMap<K,V>{
    protected BoboAbstractMap(){
    }

    public int size(){
        return entrySet().size();
    }

    public boolean isEmpty(){
        return size() == 0;
    }

    public boolean containsValue(Object value){
        BoboIterator<Entry<K,V>> i = entrySet().iterator();
        if (value == null){
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (e.getValue() == null)
                    return true;
            }
        } else {
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (value.equals(e.getValue()))
                    return true;
            }
        }
        return false;
    }

    @Override
    public boolean containsKey(Object key) {
        BoboIterator<Entry<K,V>> i = entrySet().iterator();
        if (key == null){
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (e.getKey() == null)
                    return true;
            }
        } else {
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return true;
            }
        }
        return false;
    }

    @Override
    public V get(Object key) {
        BoboIterator<Entry<K,V>> i = entrySet().iterator();
        if (key == null){
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (e.getKey() == null)
                    return e.getValue();
            }
        } else {
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return e.getValue();
            }
        }
        return null;
    }

    //需要子类重写
    @Override
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }

    //默认实现为： 利用迭代器查找，查找到就利用迭代器删除，并返回删除的value。
    //没有找到就返回null
    //这样需要遍历操作，O（N）时间复杂度，大多数类都需要重写该方法
    @Override
    public V remove(Object key) {
        BoboIterator<Entry<K,V>> i = entrySet().iterator();
        Entry<K,V> correctEntry = null;
        if (key == null){
            while (correctEntry == null && i.hasNext()){
                Entry<K,V> e = i.next();
                if (e.getKey() == null)
                    correctEntry = e;
            }
        } else {
            while (correctEntry == null && i.hasNext()){
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    correctEntry = e;
            }
        }

        V oldValue = null;
        if (correctEntry != null){
            oldValue = correctEntry.getValue();
            i.remove();         //利用迭代器删除
        }
        return oldValue;
    }

    @Override
    public void putAll(BoboMap<? extends K, ? extends V> m) {
        BoboIterator it = m.entrySet().iterator();
        while (it.hasNext()){
            Entry e = (Entry) it.next();
            put((K)e.getKey(), (V)e.getValue());
        }
    }

    @Override
    public void clear() {
        entrySet().clear();
    }

    transient BoboSet<K> keySet;
    transient BoboCollection<V> values;

    @Override
    public BoboSet<K> keySet() {
        BoboSet<K> ks = keySet;
        if (ks == null){
            ks = new BoboAbstractSet<K>() {
                @Override
                public int size() {
                    return BoboAbstractMap.this.size();
                }

                public void clear(){
                    BoboAbstractMap.this.clear();
                }
                public boolean contains(Object k){
                    return BoboAbstractMap.this.containsKey(k);
                }

                @Override
                public boolean addAll(BoboCollection<? extends K> c) {
                    return false;
                }

                @Override
                public BoboIterator<K> iterator() {
                    return new BoboIterator<K>() {
                        private BoboIterator<Entry<K,V>> i = entrySet().iterator();
                        @Override
                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        @Override
                        public K next() {
                            return i.next().getKey();
                        }

                        @Override
                        public void remove() {
                            i.remove();
                        }
                    };
                }

                public boolean isEmpty(){
                    return BoboAbstractMap.this.isEmpty();
                }
            };
            keySet = ks;
        }
        return ks;
    }

    @Override
    public BoboCollection<V> values() {
        return null;
    }


    public abstract BoboSet<Entry<K, V>> entrySet();

    public boolean equals(Object o){
        if (o == this){
            return true;
        }
        if (!(o instanceof BoboMap)){
            return false;
        }

        BoboMap<?,?> m = (BoboMap<?,?>) o;

        if (m.size() != size()){
            return false;
        }

        try {
            BoboIterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()){
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null){
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

    public int hashCode(){
        int h = 0;
        BoboIterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext()){
            h += i.next().hashCode();
        }
        return h;
    }
}
